//
// Created by sp on 19-7-4.
//

#ifndef ATTACK_TOML_H
#define ATTACK_TOML_H

#include <algorithm>
#include <cassert>
#include <cctype>
#include <chrono>
#include <cmath>
#include <cstdint>
#include <cstdio>
#include <ctime>
#include <fstream>
#include <iomanip>
#include <istream>
#include <sstream>
#include <stdexcept>
#include <string>
#include <map>
#include <memory>
#include <utility>
#include <vector>

namespace toml {

// ----------------------------------------------------------------------
// Declarations

    class Value;
    typedef std::chrono::system_clock::time_point Time;
    typedef std::vector<Value> Array;
    typedef std::map<std::string, Value> Table;

    namespace internal {
        template<typename T> struct call_traits_value {
            typedef T return_type;
        };
        template<typename T> struct call_traits_ref {
            typedef const T& return_type;
        };
    } // namespace internal

    template<typename T> struct call_traits;
    template<> struct call_traits<bool> : public internal::call_traits_value<bool> {};
    template<> struct call_traits<int> : public internal::call_traits_value<int> {};
    template<> struct call_traits<int64_t> : public internal::call_traits_value<int64_t> {};
    template<> struct call_traits<double> : public internal::call_traits_value<double> {};
    template<> struct call_traits<std::string> : public internal::call_traits_ref<std::string> {};
    template<> struct call_traits<Time> : public internal::call_traits_ref<Time> {};
    template<> struct call_traits<Array> : public internal::call_traits_ref<Array> {};
    template<> struct call_traits<Table> : public internal::call_traits_ref<Table> {};

// A value is returned for std::vector<T>. Not reference.
// This is because a fresh vector is made.
    template<typename T> struct call_traits<std::vector<T>> : public internal::call_traits_value<std::vector<T>> {};

// Formatting flags
    enum FormatFlag {
        FORMAT_NONE = 0,
        FORMAT_INDENT = 1
    };

    class Value {
    public:
        enum Type {
            NULL_TYPE,
            BOOL_TYPE,
            INT_TYPE,
            DOUBLE_TYPE,
            STRING_TYPE,
            TIME_TYPE,
            ARRAY_TYPE,
            TABLE_TYPE,
        };

        Value() : type_(NULL_TYPE), null_(nullptr) {}
        Value(bool v) : type_(BOOL_TYPE), bool_(v) {}
        Value(int v) : type_(INT_TYPE), int_(v) {}
        Value(int64_t v) : type_(INT_TYPE), int_(v) {}
        Value(double v) : type_(DOUBLE_TYPE), double_(v) {}
        Value(const std::string& v) : type_(STRING_TYPE), string_(new std::string(v)) {}
        Value(const char* v) : type_(STRING_TYPE), string_(new std::string(v)) {}
        Value(const Time& v) : type_(TIME_TYPE), time_(new Time(v)) {}
        Value(const Array& v) : type_(ARRAY_TYPE), array_(new Array(v)) {}
        Value(const Table& v) : type_(TABLE_TYPE), table_(new Table(v)) {}
        Value(std::string&& v) : type_(STRING_TYPE), string_(new std::string(std::move(v))) {}
        Value(Array&& v) : type_(ARRAY_TYPE), array_(new Array(std::move(v))) {}
        Value(Table&& v) : type_(TABLE_TYPE), table_(new Table(std::move(v))) {}

        Value(const Value& v);
        Value(Value&& v) noexcept;
        Value& operator=(const Value& v);
        Value& operator=(Value&& v) noexcept;

        // Guards from unexpected Value construction.
        // Someone might use a value like this:
        //   toml::Value v = x->find("foo");
        // But this is wrong. Without this constructor,
        // value will be unexpectedly initialized with bool.
        Value(const void* v) = delete;
        ~Value();

        // Retruns Value size.
        // 0 for invalid value.
        // The number of inner elements for array or table.
        // 1 for other types.
        size_t size() const;
        bool empty() const;
        Type type() const { return type_; }

        bool valid() const { return type_ != NULL_TYPE; }
        template<typename T> bool is() const;
        template<typename T> typename call_traits<T>::return_type as() const;

        friend bool operator==(const Value& lhs, const Value& rhs);
        friend bool operator!=(const Value& lhs, const Value& rhs) { return !(lhs == rhs); }

        // ----------------------------------------------------------------------
        // For integer/floating value

        // Returns true if the value is int or double.
        bool isNumber() const;
        // Returns number. Convert to double.
        double asNumber() const;

        // ----------------------------------------------------------------------
        // For Time value

        // Converts to time_t if the internal value is Time.
        // We don't have as<std::time_t>(). Since time_t is basically signed long,
        // it's something like a method to converting to (normal) integer.
        std::time_t as_time_t() const;

        // ----------------------------------------------------------------------
        // For Table value
        template<typename T> typename call_traits<T>::return_type get(const std::string&) const;
        Value* set(const std::string& key, const Value& v);
        // Finds a Value with |key|. |key| can contain '.'
        // Note: if you would like to find a child value only, you need to use findChild.
        const Value* find(const std::string& key) const;
        Value* find(const std::string& key);
        bool has(const std::string& key) const { return find(key) != nullptr; }
        bool erase(const std::string& key);

        Value& operator[](const std::string& key);

        // Merge table. Returns true if succeeded. Otherwise, |this| might be corrupted.
        // When the same key exists, it will be overwritten.
        bool merge(const Value&);

        // Finds a value with |key|. It searches only children.
        Value* findChild(const std::string& key);
        const Value* findChild(const std::string& key) const;
        // Sets a value, and returns the pointer to the created value.
        // When the value having the same key exists, it will be overwritten.
        Value* setChild(const std::string& key, const Value& v);
        Value* setChild(const std::string& key, Value&& v);
        bool eraseChild(const std::string& key);

        // ----------------------------------------------------------------------
        // For Array value

        template<typename T> typename call_traits<T>::return_type get(size_t index) const;
        const Value* find(size_t index) const;
        Value* find(size_t index);
        Value* push(const Value& v);
        Value* push(Value&& v);

        // ----------------------------------------------------------------------
        // Others

        // Writer.
        static std::string spaces(int num);
        static std::string escapeKey(const std::string& key);

        void write(std::ostream*, const std::string& keyPrefix = std::string(), int indent = -1) const;
        void writeFormatted(std::ostream*, FormatFlag flags) const;

        friend std::ostream& operator<<(std::ostream&, const Value&);

    private:
        static const char* typeToString(Type);

        template<typename T> void assureType() const;
        Value* ensureValue(const std::string& key);

        template<typename T> struct ValueConverter;

        Type type_;
        union {
            void* null_;
            bool bool_;
            int64_t int_;
            double double_;
            std::string* string_;
            Time* time_;
            Array* array_;
            Table* table_;
        };

        template<typename T> friend struct ValueConverter;
    };

// parse() returns ParseResult.
    struct ParseResult {
        ParseResult(toml::Value v, std::string er) :
                value(std::move(v)),
                errorReason(std::move(er)) {}

        bool valid() const { return value.valid(); }

        toml::Value value;
        std::string errorReason;
    };

// Parses from std::istream.
    ParseResult parse(std::istream&);
// Parses a file.
    ParseResult parseFile(const std::string& filename);

// ----------------------------------------------------------------------
// Declarations for Implementations
//   You don't need to understand the below to use this library.

#if defined(_WIN32)
    // Windows does not have timegm but have _mkgmtime.
inline time_t timegm(std::tm* timeptr)
{
    return _mkgmtime(timeptr);
}

// On Windows, Visual Studio does not define gmtime_r. However, mingw might
// do (or might not do). See https://github.com/mayah/tinytoml/issues/25,
#ifndef gmtime_r
inline struct tm* gmtime_r(const time_t* t, struct tm* r)
{
    // gmtime is threadsafe in windows because it uses TLS
    struct tm *theTm = gmtime(t);
    if (theTm) {
        *r = *theTm;
        return r;
    } else {
        return 0;
    }
}
#endif  // gmtime_r
#endif  // _WIN32

    namespace internal {

        enum class TokenType {
            ERROR_TOKEN,
            END_OF_FILE,
            END_OF_LINE,
            IDENT,
            STRING,
            MULTILINE_STRING,
            BOOL,
            INT,
            DOUBLE,
            TIME,
            COMMA,
            DOT,
            EQUAL,
            LBRACKET,
            RBRACKET,
            LBRACE,
            RBRACE,
        };

        class Token {
        public:
            explicit Token(TokenType tokenType) : type_(tokenType) {}
            Token(TokenType tokenType, const std::string& v) : type_(tokenType), str_value_(v) {}
            Token(TokenType tokenType, bool v) : type_(tokenType), int_value_(v) {}
            Token(TokenType tokenType, std::int64_t v) : type_(tokenType), int_value_(v) {}
            Token(TokenType tokenType, double v) : type_(tokenType), double_value_(v) {}
            Token(TokenType tokenType, std::chrono::system_clock::time_point tp) : type_(tokenType), time_value_(tp) {}

            TokenType type() const { return type_; }
            const std::string& strValue() const { return str_value_; }
            bool boolValue() const { return int_value_ != 0; }
            std::int64_t intValue() const { return int_value_; }
            double doubleValue() const { return double_value_; }
            std::chrono::system_clock::time_point timeValue() const { return time_value_; }

        private:
            TokenType type_;
            std::string str_value_;
            std::int64_t int_value_;
            double double_value_;
            std::chrono::system_clock::time_point time_value_;
        };

        class Lexer {
        public:
            explicit Lexer(std::istream& is) : is_(is), lineNo_(1) {}

            Token nextKeyToken();
            Token nextValueToken();

            int lineNo() const { return lineNo_; }

            // Skips if UTF8BOM is found.
            // Returns true if success. Returns false if intermediate state is left.
            bool skipUTF8BOM();

        private:
            bool current(char* c);
            void next();
            bool consume(char c);

            Token nextToken(bool isValueToken);

            void skipUntilNewLine();

            Token nextStringDoubleQuote();
            Token nextStringSingleQuote();

            Token nextKey();
            Token nextValue();

            Token parseAsTime(const std::string&);

            std::istream& is_;
            int lineNo_;
        };

        class Parser {
        public:
            explicit Parser(std::istream& is) : lexer_(is), token_(TokenType::ERROR_TOKEN)
            {
                if (!lexer_.skipUTF8BOM()) {
                    token_ = Token(TokenType::ERROR_TOKEN, std::string("Invalid UTF8 BOM"));
                } else {
                    nextKey();
                }
            }

            // Parses. If failed, value should be invalid value.
            // You can get the error by calling errorReason().
            Value parse();
            const std::string& errorReason();

        private:
            const Token& token() const { return token_; }
            void nextKey() { token_ = lexer_.nextKeyToken(); }
            void nextValue() { token_ = lexer_.nextValueToken(); }

            void skipForKey();
            void skipForValue();

            bool consumeForKey(TokenType);
            bool consumeForValue(TokenType);
            bool consumeEOLorEOFForKey();

            Value* parseGroupKey(Value* root);

            bool parseKeyValue(Value*);
            bool parseKey(std::string*);
            bool parseValue(Value*);
            bool parseBool(Value*);
            bool parseNumber(Value*);
            bool parseArray(Value*);
            bool parseInlineTable(Value*);

            void addError(const std::string& reason);

            Lexer lexer_;
            Token token_;
            std::string errorReason_;
        };

    } // namespace internal

// ----------------------------------------------------------------------
// Implementations

    inline ParseResult parse(std::istream& is)
    {
        if (!is) {
            return ParseResult(toml::Value(), "stream is in bad state. file does not exist?");
        }

        internal::Parser parser(is);
        toml::Value v = parser.parse();

        if (v.valid())
            return ParseResult(std::move(v), std::string());

        return ParseResult(std::move(v), std::move(parser.errorReason()));
    }

    inline ParseResult parseFile(const std::string& filename)
    {
        std::ifstream ifs(filename);
        if (!ifs) {
            return ParseResult(toml::Value(),
                               std::string("could not open file: ") + filename);
        }

        return parse(ifs);
    }

    inline std::string format(std::stringstream& ss)
    {
        return ss.str();
    }

    template<typename T, typename... Args>
    std::string format(std::stringstream& ss, T&& t, Args&&... args)
    {
        ss << std::forward<T>(t);
        return format(ss, std::forward<Args>(args)...);
    }

// If you want to compile without exception,
//   1. Define TOML_HAVE_FAILWITH_REPLACEMENT
//   2. Define your own toml::failwith.
// e.g. You can just abort here instead of exception.
#ifndef TOML_HAVE_FAILWITH_REPLACEMENT

    template<typename... Args>
#if defined(_MSC_VER)
    __declspec(noreturn)
#else
    [[noreturn]]
#endif
    void failwith(Args&&... args)
    {
        std::stringstream ss;
        throw std::runtime_error(format(ss, std::forward<Args>(args)...));
    }

#endif

    namespace internal {

        inline std::string removeDelimiter(const std::string& s)
        {
            std::string r;
            for (char c : s) {
                if (c == '_')
                    continue;
                r += c;
            }
            return r;
        }

        inline std::string unescape(const std::string& codepoint)
        {
            std::uint32_t x;
            std::uint8_t buf[8];
            std::stringstream ss(codepoint);

            ss >> std::hex >> x;

            if (x <= 0x7FUL) {
                // 0xxxxxxx
                buf[0] = 0x00 | ((x >> 0) & 0x7F);
                buf[1] = '\0';
            } else if (x <= 0x7FFUL) {
                // 110yyyyx 10xxxxxx
                buf[0] = 0xC0 | ((x >> 6) & 0xDF);
                buf[1] = 0x80 | ((x >> 0) & 0xBF);
                buf[2] = '\0';
            } else if (x <= 0xFFFFUL) {
                // 1110yyyy 10yxxxxx 10xxxxxx
                buf[0] = 0xE0 | ((x >> 12) & 0xEF);
                buf[1] = 0x80 | ((x >> 6) & 0xBF);
                buf[2] = 0x80 | ((x >> 0) & 0xBF);
                buf[3] = '\0';
            } else if (x <= 0x10FFFFUL) {
                // 11110yyy 10yyxxxx 10xxxxxx 10xxxxxx
                buf[0] = 0xF0 | ((x >> 18) & 0xF7);
                buf[1] = 0x80 | ((x >> 12) & 0xBF);
                buf[2] = 0x80 | ((x >> 6) & 0xBF);
                buf[3] = 0x80 | ((x >> 0) & 0xBF);
                buf[4] = '\0';
            } else {
                buf[0] = '\0';
            }

            return reinterpret_cast<char*>(buf);
        }

// Returns true if |s| is integer.
// [+-]?\d+(_\d+)*
        inline bool isInteger(const std::string& s)
        {
            if (s.empty())
                return false;

            std::string::size_type p = 0;
            if (s[p] == '+' || s[p] == '-')
                ++p;

            while (p < s.size() && '0' <= s[p] && s[p] <= '9') {
                ++p;
                if (p < s.size() && s[p] == '_') {
                    ++p;
                    if (!(p < s.size() && '0' <= s[p] && s[p] <= '9'))
                        return false;
                }
            }

            return p == s.size();
        }

// Returns true if |s| is double.
// [+-]? (\d+(_\d+)*)? (\.\d+(_\d+)*)? ([eE] [+-]? \d+(_\d+)*)?
//       1-----------  2-------------  3----------------------
// 2 or (1 and 3) should exist.
        inline bool isDouble(const std::string& s)
        {
            if (s.empty())
                return false;

            std::string::size_type p = 0;
            if (s[p] == '+' || s[p] == '-')
                ++p;

            bool ok = false;
            while (p < s.size() && '0' <= s[p] && s[p] <= '9') {
                ++p;
                ok = true;

                if (p < s.size() && s[p] == '_') {
                    ++p;
                    if (!(p < s.size() && '0' <= s[p] && s[p] <= '9'))
                        return false;
                }
            }

            if (p < s.size() && s[p] == '.')
                ++p;

            while (p < s.size() && '0' <= s[p] && s[p] <= '9') {
                ++p;
                ok = true;

                if (p < s.size() && s[p] == '_') {
                    ++p;
                    if (!(p < s.size() && '0' <= s[p] && s[p] <= '9'))
                        return false;
                }
            }

            if (!ok)
                return false;

            ok = false;
            if (p < s.size() && (s[p] == 'e' || s[p] == 'E')) {
                ++p;
                if (p < s.size() && (s[p] == '+' || s[p] == '-'))
                    ++p;
                while (p < s.size() && '0' <= s[p] && s[p] <= '9') {
                    ++p;
                    ok = true;

                    if (p < s.size() && s[p] == '_') {
                        ++p;
                        if (!(p < s.size() && '0' <= s[p] && s[p] <= '9'))
                            return false;
                    }
                }
                if (!ok)
                    return false;
            }

            return p == s.size();
        }

// static
        inline std::string escapeString(const std::string& s)
        {
            std::stringstream ss;
            for (size_t i = 0; i < s.size(); ++i) {
                switch (s[i]) {
                    case '\n': ss << "\\n"; break;
                    case '\r': ss << "\\r"; break;
                    case '\t': ss << "\\t"; break;
                    case '\"': ss << "\\\""; break;
                    case '\'': ss << "\\\'"; break;
                    case '\\': ss << "\\\\"; break;
                    default: ss << s[i]; break;
                }
            }

            return ss.str();
        }

    } // namespace internal

// ----------------------------------------------------------------------
// Lexer

    namespace internal {

        inline bool Lexer::skipUTF8BOM()
        {
            // Check [EF, BB, BF]

            int x1 = is_.peek();
            if (x1 != 0xEF) {
                // When the first byte is not 0xEF, it's not UTF8 BOM.
                // Just return true.
                return true;
            }

            is_.get();
            int x2 = is_.get();
            if (x2 != 0xBB) {
                return false;
            }

            int x3 = is_.get();
            if (x3 != 0xBF) {
                return false;
            }

            return true;
        }

        inline bool Lexer::current(char* c)
        {
            int x = is_.peek();
            if (x == EOF)
                return false;
            *c = static_cast<char>(x);
            return true;
        }

        inline void Lexer::next()
        {
            int x = is_.get();
            if (x == '\n')
                ++lineNo_;
        }

        inline bool Lexer::consume(char c)
        {
            char x;
            if (!current(&x))
                return false;
            if (x != c)
                return false;
            next();
            return true;
        }

        inline void Lexer::skipUntilNewLine()
        {
            char c;
            while (current(&c)) {
                if (c == '\n')
                    return;
                next();
            }
        }

        inline Token Lexer::nextStringDoubleQuote()
        {
            if (!consume('"'))
                return Token(TokenType::ERROR_TOKEN, std::string("string didn't start with '\"'"));

            std::string s;
            char c;
            bool multiline = false;

            if (current(&c) && c == '"') {
                next();
                if (!current(&c) || c != '"') {
                    // OK. It's empty string.
                    return Token(TokenType::STRING, std::string());
                }

                next();
                // raw string literal started.
                // Newline just after """ should be ignored.
                while (current(&c) && (c == ' ' || c == '\t'))
                    next();
                if (current(&c) && c == '\n')
                    next();
                multiline = true;
            }

            while (current(&c)) {
                next();
                if (c == '\\') {
                    if (!current(&c))
                        return Token(TokenType::ERROR_TOKEN, std::string("string has unknown escape sequence"));
                    next();
                    switch (c) {
                        case 't': c = '\t'; break;
                        case 'n': c = '\n'; break;
                        case 'r': c = '\r'; break;
                        case 'u':
                        case 'U': {
                            int size = c == 'u' ? 4 : 8;
                            std::string codepoint;
                            for (int i = 0; i < size; ++i) {
                                if (current(&c) && (('0' <= c && c <= '9') || ('A' <= c && c <= 'F') || ('a' <= c && c <= 'f'))) {
                                    codepoint += c;
                                    next();
                                } else {
                                    return Token(TokenType::ERROR_TOKEN, std::string("string has unknown escape sequence"));
                                }
                            }
                            s += unescape(codepoint);
                            continue;
                        }
                        case '"': c = '"'; break;
                        case '\'': c = '\''; break;
                        case '\\': c = '\\'; break;
                        case '\n':
                            while (current(&c) && (c == ' ' || c == '\t' || c == '\r' || c == '\n')) {
                                next();
                            }
                            continue;
                        default:
                            return Token(TokenType::ERROR_TOKEN, std::string("string has unknown escape sequence"));
                    }
                } else if (c == '"') {
                    if (multiline) {
                        if (current(&c) && c == '"') {
                            next();
                            if (current(&c) && c == '"') {
                                next();
                                return Token(TokenType::MULTILINE_STRING, s);
                            } else {
                                s += '"';
                                s += '"';
                                continue;
                            }
                        } else {
                            s += '"';
                            continue;
                        }
                    } else {
                        return Token(TokenType::STRING, s);
                    }
                }

                s += c;
            }

            return Token(TokenType::ERROR_TOKEN, std::string("string didn't end"));
        }

        inline Token Lexer::nextStringSingleQuote()
        {
            if (!consume('\''))
                return Token(TokenType::ERROR_TOKEN, std::string("string didn't start with '\''?"));

            std::string s;
            char c;

            if (current(&c) && c == '\'') {
                next();
                if (!current(&c) || c != '\'') {
                    // OK. It's empty string.
                    return Token(TokenType::STRING, std::string());
                }
                next();
                // raw string literal started.
                // Newline just after """ should be ignored.
                if (current(&c) && c == '\n')
                    next();

                while (current(&c)) {
                    if (c == '\'') {
                        next();
                        if (current(&c) && c == '\'') {
                            next();
                            if (current(&c) && c == '\'') {
                                next();
                                return Token(TokenType::MULTILINE_STRING, s);
                            } else {
                                s += '\'';
                                s += '\'';
                                continue;
                            }
                        } else {
                            s += '\'';
                            continue;
                        }
                    }

                    next();
                    s += c;
                    continue;
                }

                return Token(TokenType::ERROR_TOKEN, std::string("string didn't end with '\'\'\'' ?"));
            }

            while (current(&c)) {
                next();
                if (c == '\'') {
                    return Token(TokenType::STRING, s);
                }

                s += c;
            }

            return Token(TokenType::ERROR_TOKEN, std::string("string didn't end with '\''?"));
        }

        inline Token Lexer::nextKey()
        {
            std::string s;
            char c;
            while (current(&c) && (isalnum(static_cast<unsigned char>(c)) || c == '_' || c == '-')) {
                s += c;
                next();
            }

            if (s.empty())
                return Token(TokenType::ERROR_TOKEN, std::string("Unknown key format"));

            return Token(TokenType::IDENT, s);
        }

        inline Token Lexer::nextValue()
        {
            std::string s;
            char c;

            if (current(&c) && isalpha(static_cast<unsigned char>(c))) {
                s += c;
                next();
                while (current(&c) && isalpha(static_cast<unsigned char>(c))) {
                    s += c;
                    next();
                }

                if (s == "true")
                    return Token(TokenType::BOOL, true);
                if (s == "false")
                    return Token(TokenType::BOOL, false);
                return Token(TokenType::ERROR_TOKEN, std::string("Unknown ident: ") + s);
            }

            while (current(&c) && (('0' <= c && c <= '9') || c == '.' || c == 'e' || c == 'E' ||
                                   c == 'T' || c == 'Z' || c == '_' || c == ':' || c == '-' || c == '+')) {
                next();
                s += c;
            }

            if (isInteger(s)) {
                std::stringstream ss(removeDelimiter(s));
                std::int64_t x;
                ss >> x;
                return Token(TokenType::INT, x);
            }

            if (isDouble(s)) {
                std::stringstream ss(removeDelimiter(s));
                double d;
                ss >> d;
                return Token(TokenType::DOUBLE, d);
            }

            return parseAsTime(s);
        }

        inline Token Lexer::parseAsTime(const std::string& str)
        {
            const char* s = str.c_str();

            int n;
            int YYYY, MM, DD;
#if defined(_MSC_VER)
            if (sscanf_s(s, "%d-%d-%d%n", &YYYY, &MM, &DD, &n) != 3)
        return Token(TokenType::ERROR_TOKEN, std::string("Invalid token"));
#else
            if (sscanf(s, "%d-%d-%d%n", &YYYY, &MM, &DD, &n) != 3)
                return Token(TokenType::ERROR_TOKEN, std::string("Invalid token"));
#endif

            if (!(1 <= MM && MM <= 12)) {
                return Token(TokenType::ERROR_TOKEN, std::string("Invalid token"));
            }
            if (YYYY < 1900) {
                return Token(TokenType::ERROR_TOKEN, std::string("Invalid token"));
            }

            if (s[n] == '\0') {
                std::tm t;
                t.tm_sec = 0;
                t.tm_min = 0;
                t.tm_hour = 0;
                t.tm_mday = DD;
                t.tm_mon = MM - 1;
                t.tm_year = YYYY - 1900;
                auto tp = std::chrono::system_clock::from_time_t(timegm(&t));
                return Token(TokenType::TIME, tp);
            }

            if (s[n] != 'T')
                return Token(TokenType::ERROR_TOKEN, std::string("Invalid token"));

            s = s + n + 1;

            int hh, mm;
            double ss; // double for fraction
#if defined(_MSC_VER)
            if (sscanf_s(s, "%d:%d:%lf%n", &hh, &mm, &ss, &n) != 3)
        return Token(TokenType::ERROR_TOKEN, std::string("Invalid token"));
#else
            if (sscanf(s, "%d:%d:%lf%n", &hh, &mm, &ss, &n) != 3)
                return Token(TokenType::ERROR_TOKEN, std::string("Invalid token"));
#endif

            std::tm t;
            t.tm_sec = static_cast<int>(ss);
            t.tm_min = mm;
            t.tm_hour = hh;
            t.tm_mday = DD;
            t.tm_mon = MM - 1;
            t.tm_year = YYYY - 1900;
            auto tp = std::chrono::system_clock::from_time_t(timegm(&t));
            ss -= static_cast<int>(ss);
            // TODO(mayah): workaround GCC 4.9.3 on cygwin does not have std::round, but round().
            tp += std::chrono::microseconds(static_cast<std::int64_t>(round(ss * 1000000)));

            if (s[n] == '\0')
                return Token(TokenType::TIME, tp);

            if (s[n] == 'Z' && s[n + 1] == '\0')
                return Token(TokenType::TIME, tp);

            s = s + n;
            // offset
            // [+/-]%d:%d
            char pn;
            int oh, om;
#if defined(_MSC_VER)
            if (sscanf_s(s, "%c%d:%d", &pn, static_cast<unsigned>(sizeof(pn)), &oh, &om) != 3)
        return Token(TokenType::ERROR_TOKEN, std::string("Invalid token"));
#else
            if (sscanf(s, "%c%d:%d", &pn, &oh, &om) != 3)
                return Token(TokenType::ERROR_TOKEN, std::string("Invalid token"));
#endif

            if (pn != '+' && pn != '-')
                return Token(TokenType::ERROR_TOKEN, std::string("Invalid token"));

            if (pn == '+') {
                tp -= std::chrono::hours(oh);
                tp -= std::chrono::minutes(om);
            } else {
                tp += std::chrono::hours(oh);
                tp += std::chrono::minutes(om);
            }

            return Token(TokenType::TIME, tp);
        }

        inline Token Lexer::nextKeyToken()
        {
            return nextToken(false);
        }

        inline Token Lexer::nextValueToken()
        {
            return nextToken(true);
        }

        inline Token Lexer::nextToken(bool isValueToken)
        {
            char c;
            while (current(&c)) {
                if (c == ' ' || c == '\t' || c == '\r') {
                    next();
                    continue;
                }

                if (c == '#') {
                    skipUntilNewLine();
                    continue;
                }

                switch (c) {
                    case '\n':
                        next();
                        return Token(TokenType::END_OF_LINE);
                    case '=':
                        next();
                        return Token(TokenType::EQUAL);
                    case '{':
                        next();
                        return Token(TokenType::LBRACE);
                    case '}':
                        next();
                        return Token(TokenType::RBRACE);
                    case '[':
                        next();
                        return Token(TokenType::LBRACKET);
                    case ']':
                        next();
                        return Token(TokenType::RBRACKET);
                    case ',':
                        next();
                        return Token(TokenType::COMMA);
                    case '.':
                        next();
                        return Token(TokenType::DOT);
                    case '\"':
                        return nextStringDoubleQuote();
                    case '\'':
                        return nextStringSingleQuote();
                    default:
                        if (isValueToken) {
                            return nextValue();
                        } else {
                            return nextKey();
                        }
                }
            }

            return Token(TokenType::END_OF_FILE);
        }

    } // namespace internal

// ----------------------------------------------------------------------

// static
    inline const char* Value::typeToString(Value::Type type)
    {
        switch (type) {
            case NULL_TYPE:   return "null";
            case BOOL_TYPE:   return "bool";
            case INT_TYPE:    return "int";
            case DOUBLE_TYPE: return "double";
            case STRING_TYPE: return "string";
            case TIME_TYPE:   return "time";
            case ARRAY_TYPE:  return "array";
            case TABLE_TYPE:  return "table";
            default:          return "unknown";
        }
    }

    inline Value::Value(const Value& v) :
            type_(v.type_)
    {
        switch (v.type_) {
            case NULL_TYPE: null_ = v.null_; break;
            case BOOL_TYPE: bool_ = v.bool_; break;
            case INT_TYPE: int_ = v.int_; break;
            case DOUBLE_TYPE: double_ = v.double_; break;
            case STRING_TYPE: string_ = new std::string(*v.string_); break;
            case TIME_TYPE: time_ = new Time(*v.time_); break;
            case ARRAY_TYPE: array_ = new Array(*v.array_); break;
            case TABLE_TYPE: table_ = new Table(*v.table_); break;
            default:
                assert(false);
                type_ = NULL_TYPE;
                null_ = nullptr;
        }
    }

    inline Value::Value(Value&& v) noexcept :
            type_(v.type_)
    {
        switch (v.type_) {
            case NULL_TYPE: null_ = v.null_; break;
            case BOOL_TYPE: bool_ = v.bool_; break;
            case INT_TYPE: int_ = v.int_; break;
            case DOUBLE_TYPE: double_ = v.double_; break;
            case STRING_TYPE: string_ = v.string_; break;
            case TIME_TYPE: time_ = v.time_; break;
            case ARRAY_TYPE: array_ = v.array_; break;
            case TABLE_TYPE: table_ = v.table_; break;
            default:
                assert(false);
                type_ = NULL_TYPE;
                null_ = nullptr;
        }

        v.type_ = NULL_TYPE;
        v.null_ = nullptr;
    }

    inline Value& Value::operator=(const Value& v)
    {
        if (this == &v)
            return *this;

        this->~Value();

        type_ = v.type_;
        switch (v.type_) {
            case NULL_TYPE: null_ = v.null_; break;
            case BOOL_TYPE: bool_ = v.bool_; break;
            case INT_TYPE: int_ = v.int_; break;
            case DOUBLE_TYPE: double_ = v.double_; break;
            case STRING_TYPE: string_ = new std::string(*v.string_); break;
            case TIME_TYPE: time_ = new Time(*v.time_); break;
            case ARRAY_TYPE: array_ = new Array(*v.array_); break;
            case TABLE_TYPE: table_ = new Table(*v.table_); break;
            default:
                assert(false);
                type_ = NULL_TYPE;
                null_ = nullptr;
        }

        return *this;
    }

    inline Value& Value::operator=(Value&& v) noexcept
    {
        if (this == &v)
            return *this;

        this->~Value();

        type_ = v.type_;
        switch (v.type_) {
            case NULL_TYPE: null_ = v.null_; break;
            case BOOL_TYPE: bool_ = v.bool_; break;
            case INT_TYPE: int_ = v.int_; break;
            case DOUBLE_TYPE: double_ = v.double_; break;
            case STRING_TYPE: string_ = v.string_; break;
            case TIME_TYPE: time_ = v.time_; break;
            case ARRAY_TYPE: array_ = v.array_; break;
            case TABLE_TYPE: table_ = v.table_; break;
            default:
                assert(false);
                type_ = NULL_TYPE;
                null_ = nullptr;
        }

        v.type_ = NULL_TYPE;
        v.null_ = nullptr;
        return *this;
    }

    inline Value::~Value()
    {
        switch (type_) {
            case STRING_TYPE:
                delete string_;
                break;
            case TIME_TYPE:
                delete time_;
                break;
            case ARRAY_TYPE:
                delete array_;
                break;
            case TABLE_TYPE:
                delete table_;
                break;
            default:
                break;
        }
    }

    inline size_t Value::size() const
    {
        switch (type_) {
            case NULL_TYPE:
                return 0;
            case ARRAY_TYPE:
                return array_->size();
            case TABLE_TYPE:
                return table_->size();
            default:
                return 1;
        }
    }

    inline bool Value::empty() const
    {
        return size() == 0;
    }

    template<> struct Value::ValueConverter<bool>
    {
        bool is(const Value& v) { return v.type() == Value::BOOL_TYPE; }
        bool to(const Value& v) { v.assureType<bool>(); return v.bool_; }

    };
    template<> struct Value::ValueConverter<int64_t>
    {
        bool is(const Value& v) { return v.type() == Value::INT_TYPE; }
        int64_t to(const Value& v) { v.assureType<int64_t>(); return v.int_; }
    };
    template<> struct Value::ValueConverter<int>
    {
        bool is(const Value& v) { return v.type() == Value::INT_TYPE; }
        int to(const Value& v) { v.assureType<int>(); return static_cast<int>(v.int_); }
    };
    template<> struct Value::ValueConverter<double>
    {
        bool is(const Value& v) { return v.type() == Value::DOUBLE_TYPE; }
        double to(const Value& v) { v.assureType<double>(); return v.double_; }
    };
    template<> struct Value::ValueConverter<std::string>
    {
        bool is(const Value& v) { return v.type() == Value::STRING_TYPE; }
        const std::string& to(const Value& v) { v.assureType<std::string>(); return *v.string_; }
    };
    template<> struct Value::ValueConverter<Time>
    {
        bool is(const Value& v) { return v.type() == Value::TIME_TYPE; }
        const Time& to(const Value& v) { v.assureType<Time>(); return *v.time_; }
    };
    template<> struct Value::ValueConverter<Array>
    {
        bool is(const Value& v) { return v.type() == Value::ARRAY_TYPE; }
        const Array& to(const Value& v) { v.assureType<Array>(); return *v.array_; }
    };
    template<> struct Value::ValueConverter<Table>
    {
        bool is(const Value& v) { return v.type() == Value::TABLE_TYPE; }
        const Table& to(const Value& v) { v.assureType<Table>(); return *v.table_; }
    };

    template<typename T>
    struct Value::ValueConverter<std::vector<T>>
    {
        bool is(const Value& v)
        {
            if (v.type() != Value::ARRAY_TYPE)
                return false;
            const Array& array = v.as<Array>();
            if (array.empty())
                return true;
            return array.front().is<T>();
        }

        std::vector<T> to(const Value& v)
        {
            const Array& array = v.as<Array>();
            if (array.empty())
                return std::vector<T>();
            array.front().assureType<T>();

            std::vector<T> result;
            for (const auto& element : array) {
                result.push_back(element.as<T>());
            }

            return result;
        }
    };

    namespace internal {
        template<typename T> inline const char* type_name();
        template<> inline const char* type_name<bool>() { return "bool"; }
        template<> inline const char* type_name<int>() { return "int"; }
        template<> inline const char* type_name<int64_t>() { return "int64_t"; }
        template<> inline const char* type_name<double>() { return "double"; }
        template<> inline const char* type_name<std::string>() { return "string"; }
        template<> inline const char* type_name<toml::Time>() { return "time"; }
        template<> inline const char* type_name<toml::Array>() { return "array"; }
        template<> inline const char* type_name<toml::Table>() { return "table"; }
    } // namespace internal

    template<typename T>
    inline void Value::assureType() const
    {
        if (!is<T>())
            failwith("type error: this value is ", typeToString(type_), " but ", internal::type_name<T>(), " was requested");
    }

    template<typename T>
    inline bool Value::is() const
    {
        return ValueConverter<T>().is(*this);
    }

    template<typename T>
    inline typename call_traits<T>::return_type Value::as() const
    {
        return ValueConverter<T>().to(*this);
    }

    inline bool Value::isNumber() const
    {
        return is<int>() || is<double>();
    }

    inline double Value::asNumber() const
    {
        if (is<int>())
            return as<int>();
        if (is<double>())
            return as<double>();

        failwith("type error: this value is ", typeToString(type_), " but number is requested");
    }

    inline std::time_t Value::as_time_t() const
    {
        return std::chrono::system_clock::to_time_t(as<Time>());
    }

    inline std::string Value::spaces(int num)
    {
        if (num <= 0)
            return std::string();

        return std::string(num, ' ');
    }

    inline std::string Value::escapeKey(const std::string& key)
    {
        auto position = std::find_if(key.begin(), key.end(), [](char c) -> bool {
            if (std::isalnum(static_cast<unsigned char>(c)) || c == '_' || c == '-')
                return false;
            return true;
        });

        if (position != key.end()) {
            std::string escaped = "\"";
            for (const char& c : key) {
                if (c == '\\' || c  == '"')
                    escaped += '\\';
                escaped += c;
            }
            escaped += "\"";

            return escaped;
        }

        return key;
    }

    inline void Value::write(std::ostream* os, const std::string& keyPrefix, int indent) const
    {
        switch (type_) {
            case NULL_TYPE:
                failwith("null type value is not a valid value");
                break;
            case BOOL_TYPE:
                (*os) << (bool_ ? "true" : "false");
                break;
            case INT_TYPE:
                (*os) << int_;
                break;
            case DOUBLE_TYPE: {
                (*os) << std::fixed << std::showpoint << double_;
                break;
            }
            case STRING_TYPE:
                (*os) << '"' << internal::escapeString(*string_) << '"';
                break;
            case TIME_TYPE: {
                time_t tt = std::chrono::system_clock::to_time_t(*time_);
                std::tm t;
                gmtime_r(&tt, &t);
                char buf[256];
                snprintf(buf, sizeof(buf), "%04d-%02d-%02dT%02d:%02d:%02dZ", t.tm_year + 1900, t.tm_mon + 1, t.tm_mday, t.tm_hour, t.tm_min, t.tm_sec);
                (*os) << buf;
                break;
            }
            case ARRAY_TYPE:
                (*os) << '[';
                for (size_t i = 0; i < array_->size(); ++i) {
                    if (i)
                        (*os) << ", ";
                    (*array_)[i].write(os, keyPrefix, -1);
                }
                (*os) << ']';
                break;
            case TABLE_TYPE:
                for (const auto& kv : *table_) {
                    if (kv.second.is<Table>())
                        continue;
                    if (kv.second.is<Array>() && kv.second.size() > 0 && kv.second.find(0)->is<Table>())
                        continue;
                    (*os) << spaces(indent) << escapeKey(kv.first) << " = ";
                    kv.second.write(os, keyPrefix, indent >= 0 ? indent + 1 : indent);
                    (*os) << '\n';
                }
                for (const auto& kv : *table_) {
                    if (kv.second.is<Table>()) {
                        std::string key(keyPrefix);
                        if (!keyPrefix.empty())
                            key += ".";
                        key += escapeKey(kv.first);
                        (*os) << "\n" << spaces(indent) << "[" << key << "]\n";
                        kv.second.write(os, key, indent >= 0 ? indent + 1 : indent);
                    }
                    if (kv.second.is<Array>() && kv.second.size() > 0 && kv.second.find(0)->is<Table>()) {
                        std::string key(keyPrefix);
                        if (!keyPrefix.empty())
                            key += ".";
                        key += escapeKey(kv.first);
                        for (const auto& v : kv.second.as<Array>()) {
                            (*os) << "\n" << spaces(indent) << "[[" << key << "]]\n";
                            v.write(os, key, indent >= 0 ? indent + 1 : indent);
                        }
                    }
                }
                break;
            default:
                failwith("writing unknown type");
                break;
        }
    }

    inline void Value::writeFormatted(std::ostream* os, FormatFlag flags) const
    {
        int indent = flags & FORMAT_INDENT ? 0 : -1;

        write(os, std::string(), indent);
    }

// static
    inline FormatFlag operator|(FormatFlag lhs, FormatFlag rhs)
    {
        return static_cast<FormatFlag>(static_cast<int>(lhs) | static_cast<int>(rhs));
    }

// static
    inline std::ostream& operator<<(std::ostream& os, const toml::Value& v)
    {
        v.write(&os);
        return os;
    }

// static
    inline bool operator==(const Value& lhs, const Value& rhs)
    {
        if (lhs.type() != rhs.type())
            return false;

        switch (lhs.type()) {
            case Value::Type::NULL_TYPE:
                return true;
            case Value::Type::BOOL_TYPE:
                return lhs.bool_ == rhs.bool_;
            case Value::Type::INT_TYPE:
                return lhs.int_ == rhs.int_;
            case Value::Type::DOUBLE_TYPE:
                return lhs.double_ == rhs.double_;
            case Value::Type::STRING_TYPE:
                return *lhs.string_ == *rhs.string_;
            case Value::Type::TIME_TYPE:
                return *lhs.time_ == *rhs.time_;
            case Value::Type::ARRAY_TYPE:
                return *lhs.array_ == *rhs.array_;
            case Value::Type::TABLE_TYPE:
                return *lhs.table_ == *rhs.table_;
            default:
                failwith("unknown type");
        }
    }

    template<typename T>
    inline typename call_traits<T>::return_type Value::get(const std::string& key) const
    {
        if (!is<Table>())
            failwith("type must be table to do get(key).");

        const Value* obj = find(key);
        if (!obj)
            failwith("key ", key, " was not found.");

        return obj->as<T>();
    }

    inline const Value* Value::find(const std::string& key) const
    {
        if (!is<Table>())
            return nullptr;

        std::istringstream ss(key);
        internal::Lexer lexer(ss);

        const Value* current = this;
        while (true) {
            internal::Token t = lexer.nextKeyToken();
            if (!(t.type() == internal::TokenType::IDENT || t.type() == internal::TokenType::STRING))
                return nullptr;

            std::string part = t.strValue();
            t = lexer.nextKeyToken();
            if (t.type() == internal::TokenType::DOT) {
                current = current->findChild(part);
                if (!current || !current->is<Table>())
                    return nullptr;
            } else if (t.type() == internal::TokenType::END_OF_FILE) {
                return current->findChild(part);
            } else {
                return nullptr;
            }
        }
    }

    inline Value* Value::find(const std::string& key)
    {
        return const_cast<Value*>(const_cast<const Value*>(this)->find(key));
    }

    inline bool Value::merge(const toml::Value& v)
    {
        if (this == &v)
            return true;
        if (!is<Table>() || !v.is<Table>())
            return false;

        for (const auto& kv : *v.table_) {
            if (Value* tmp = find(kv.first)) {
                // If both are table, we merge them.
                if (tmp->is<Table>() && kv.second.is<Table>()) {
                    if (!tmp->merge(kv.second))
                        return false;
                } else {
                    setChild(kv.first, kv.second);
                }
            } else {
                setChild(kv.first, kv.second);
            }
        }

        return true;
    }

    inline Value* Value::set(const std::string& key, const Value& v)
    {
        Value* result = ensureValue(key);
        *result = v;
        return result;
    }

    inline Value* Value::setChild(const std::string& key, const Value& v)
    {
        if (!valid())
            *this = Value((Table()));

        if (!is<Table>())
            failwith("type must be table to do set(key, v).");

        (*table_)[key] = v;
        return &(*table_)[key];
    }

    inline Value* Value::setChild(const std::string& key, Value&& v)
    {
        if (!valid())
            *this = Value((Table()));

        if (!is<Table>())
            failwith("type must be table to do set(key, v).");

        (*table_)[key] = std::move(v);
        return &(*table_)[key];
    }

    inline bool Value::erase(const std::string& key)
    {
        if (!is<Table>())
            return false;

        std::istringstream ss(key);
        internal::Lexer lexer(ss);

        Value* current = this;
        while (true) {
            internal::Token t = lexer.nextKeyToken();
            if (!(t.type() == internal::TokenType::IDENT || t.type() == internal::TokenType::STRING))
                return false;

            std::string part = t.strValue();
            t = lexer.nextKeyToken();
            if (t.type() == internal::TokenType::DOT) {
                current = current->findChild(part);
                if (!current || !current->is<Table>())
                    return false;
            } else if (t.type() == internal::TokenType::END_OF_FILE) {
                return current->eraseChild(part);
            } else {
                return false;
            }
        }
    }

    inline bool Value::eraseChild(const std::string& key)
    {
        if (!is<Table>())
            failwith("type must be table to do erase(key).");

        return table_->erase(key) > 0;
    }

    inline Value& Value::operator[](const std::string& key)
    {
        if (!valid())
            *this = Value((Table()));

        if (Value* v = findChild(key))
            return *v;

        return *setChild(key, Value());
    }

    template<typename T>
    inline typename call_traits<T>::return_type Value::get(size_t index) const
    {
        if (!is<Array>())
            failwith("type must be array to do get(index).");

        if (array_->size() <= index)
            failwith("index out of bound");

        return (*array_)[index].as<T>();
    }

    inline const Value* Value::find(size_t index) const
    {
        if (!is<Array>())
            return nullptr;
        if (index < array_->size())
            return &(*array_)[index];
        return nullptr;
    }

    inline Value* Value::find(size_t index)
    {
        return const_cast<Value*>(const_cast<const Value*>(this)->find(index));
    }

    inline Value* Value::push(const Value& v)
    {
        if (!valid())
            *this = Value((Array()));
        else if (!is<Array>())
            failwith("type must be array to do push(Value).");

        array_->push_back(v);
        return &array_->back();
    }

    inline Value* Value::push(Value&& v)
    {
        if (!valid())
            *this = Value((Array()));
        else if (!is<Array>())
            failwith("type must be array to do push(Value).");

        array_->push_back(std::move(v));
        return &array_->back();
    }

    inline Value* Value::ensureValue(const std::string& key)
    {
        if (!valid())
            *this = Value((Table()));
        if (!is<Table>()) {
            failwith("encountered non table value");
        }

        std::istringstream ss(key);
        internal::Lexer lexer(ss);

        Value* current = this;
        while (true) {
            internal::Token t = lexer.nextKeyToken();
            if (!(t.type() == internal::TokenType::IDENT || t.type() == internal::TokenType::STRING)) {
                failwith("invalid key");
            }

            std::string part = t.strValue();
            t = lexer.nextKeyToken();
            if (t.type() == internal::TokenType::DOT) {
                if (Value* candidate = current->findChild(part)) {
                    if (!candidate->is<Table>())
                        failwith("encountered non table value");

                    current = candidate;
                } else {
                    current = current->setChild(part, Table());
                }
            } else if (t.type() == internal::TokenType::END_OF_FILE) {
                if (Value* v = current->findChild(part))
                    return v;
                return current->setChild(part, Value());
            } else {
                failwith("invalid key");
            }
        }
    }

    inline Value* Value::findChild(const std::string& key)
    {
        assert(is<Table>());

        auto it = table_->find(key);
        if (it == table_->end())
            return nullptr;

        return &it->second;
    }

    inline const Value* Value::findChild(const std::string& key) const
    {
        assert(is<Table>());

        auto it = table_->find(key);
        if (it == table_->end())
            return nullptr;

        return &it->second;
    }

// ----------------------------------------------------------------------

    namespace internal {

        inline void Parser::skipForKey()
        {
            while (token().type() == TokenType::END_OF_LINE)
                nextKey();
        }

        inline void Parser::skipForValue()
        {
            while (token().type() == TokenType::END_OF_LINE)
                nextValue();
        }

        inline bool Parser::consumeForKey(TokenType type)
        {
            if (token().type() == type) {
                nextKey();
                return true;
            }

            return false;
        }

        inline bool Parser::consumeForValue(TokenType type)
        {
            if (token().type() == type) {
                nextValue();
                return true;
            }

            return false;
        }

        inline bool Parser::consumeEOLorEOFForKey()
        {
            if (token().type() == TokenType::END_OF_LINE || token().type() == TokenType::END_OF_FILE) {
                nextKey();
                return true;
            }

            return false;
        }

        inline void Parser::addError(const std::string& reason)
        {
            if (!errorReason_.empty())
                return;

            std::stringstream ss;
            ss << "Error: line " << lexer_.lineNo() << ": " << reason;
            errorReason_ = ss.str();
        }

        inline const std::string& Parser::errorReason()
        {
            return errorReason_;
        }

        inline Value Parser::parse()
        {
            Value root((Table()));
            Value* currentValue = &root;

            while (true) {
                skipForKey();
                if (token().type() == TokenType::END_OF_FILE)
                    break;
                if (token().type() == TokenType::LBRACKET) {
                    currentValue = parseGroupKey(&root);
                    if (!currentValue) {
                        addError("error when parsing group key");
                        return Value();
                    }
                    continue;
                }

                if (!parseKeyValue(currentValue)) {
                    addError("error when parsing key Value");
                    return Value();
                }
            }
            return root;
        }

        inline Value* Parser::parseGroupKey(Value* root)
        {
            if (!consumeForKey(TokenType::LBRACKET))
                return nullptr;

            bool isArray = false;
            if (token().type() == TokenType::LBRACKET) {
                nextKey();
                isArray = true;
            }

            Value* currentValue = root;
            while (true) {
                if (token().type() != TokenType::IDENT && token().type() != TokenType::STRING)
                    return nullptr;

                std::string key = token().strValue();
                nextKey();

                if (token().type() == TokenType::DOT) {
                    nextKey();
                    if (Value* candidate = currentValue->findChild(key)) {
                        if (candidate->is<Array>() && candidate->size() > 0)
                            candidate = candidate->find(candidate->size() - 1);
                        if (!candidate->is<Table>())
                            return nullptr;
                        currentValue = candidate;
                    } else {
                        currentValue = currentValue->setChild(key, Table());
                    }
                    continue;
                }

                if (token().type() == TokenType::RBRACKET) {
                    nextKey();
                    if (Value* candidate = currentValue->findChild(key)) {
                        if (isArray) {
                            if (!candidate->is<Array>())
                                return nullptr;
                            currentValue = candidate->push(Table());
                        } else {
                            if (candidate->is<Array>() && candidate->size() > 0)
                                candidate = candidate->find(candidate->size() - 1);
                            if (!candidate->is<Table>())
                                return nullptr;
                            currentValue = candidate;
                        }
                    } else {
                        if (isArray) {
                            currentValue = currentValue->setChild(key, Array());
                            currentValue = currentValue->push(Table());
                        } else {
                            currentValue = currentValue->setChild(key, Table());
                        }
                    }
                    break;
                }

                return nullptr;
            }

            if (isArray) {
                if (!consumeForKey(TokenType::RBRACKET))
                    return nullptr;
            }

            if (!consumeEOLorEOFForKey())
                return nullptr;

            return currentValue;
        }

        inline bool Parser::parseKeyValue(Value* current)
        {
            std::string key;
            if (!parseKey(&key)) {
                addError("parse key failed");
                return false;
            }
            if (!consumeForValue(TokenType::EQUAL)) {
                addError("no equal?");
                return false;
            }

            Value v;
            if (!parseValue(&v))
                return false;
            if (!consumeEOLorEOFForKey())
                return false;

            if (current->has(key)) {
                addError("Multiple same key: " + key);
                return false;
            }

            current->setChild(key, std::move(v));
            return true;
        }

        inline bool Parser::parseKey(std::string* key)
        {
            key->clear();

            if (token().type() == TokenType::IDENT || token().type() == TokenType::STRING) {
                *key = token().strValue();
                nextValue();
                return true;
            }

            return false;
        }

        inline bool Parser::parseValue(Value* v)
        {
            switch (token().type()) {
                case TokenType::STRING:
                case TokenType::MULTILINE_STRING:
                    *v = token().strValue();
                    nextValue();
                    return true;
                case TokenType::LBRACKET:
                    return parseArray(v);
                case TokenType::LBRACE:
                    return parseInlineTable(v);
                case TokenType::BOOL:
                    *v = token().boolValue();
                    nextValue();
                    return true;
                case TokenType::INT:
                    *v = token().intValue();
                    nextValue();
                    return true;
                case TokenType::DOUBLE:
                    *v = token().doubleValue();
                    nextValue();
                    return true;
                case TokenType::TIME:
                    *v = token().timeValue();
                    nextValue();
                    return true;
                case TokenType::ERROR_TOKEN:
                    addError(token().strValue());
                    return false;
                default:
                    addError("unexpected token");
                    return false;
            }
        }

        inline bool Parser::parseBool(Value* v)
        {
            if (token().strValue() == "true") {
                nextValue();
                *v = true;
                return true;
            }

            if (token().strValue() == "false") {
                nextValue();
                *v = false;
                return true;
            }

            return false;
        }

        inline bool Parser::parseArray(Value* v)
        {
            if (!consumeForValue(TokenType::LBRACKET))
                return false;

            Array a;
            while (true) {
                skipForValue();

                if (token().type() == TokenType::RBRACKET)
                    break;

                skipForValue();
                Value x;
                if (!parseValue(&x))
                    return false;

                if (!a.empty()) {
                    if (a.front().type() != x.type()) {
                        addError("type check failed");
                        return false;
                    }
                }

                a.push_back(std::move(x));
                skipForValue();
                if (token().type() == TokenType::RBRACKET)
                    break;
                if (token().type() == TokenType::COMMA)
                    nextValue();
            }

            if (!consumeForValue(TokenType::RBRACKET))
                return false;
            *v = std::move(a);
            return true;
        }

        inline bool Parser::parseInlineTable(Value* value)
        {
            // For inline table, next is KEY, so use consumeForKey here.
            if (!consumeForKey(TokenType::LBRACE))
                return false;

            Value t((Table()));
            bool first = true;
            while (true) {
                if (token().type() == TokenType::RBRACE) {
                    break;
                }

                if (!first) {
                    if (token().type() != TokenType::COMMA) {
                        addError("inline table didn't have ',' for delimiter?");
                        return false;
                    }
                    nextKey();
                }
                first = false;

                std::string key;
                if (!parseKey(&key))
                    return false;
                if (!consumeForValue(TokenType::EQUAL))
                    return false;
                Value v;
                if (!parseValue(&v))
                    return false;

                if (t.has(key)) {
                    addError("inline table has multiple same keys: key=" + key);
                    return false;
                }

                t.set(key, v);
            }

            if (!consumeForValue(TokenType::RBRACE))
                return false;
            *value = std::move(t);
            return true;
        }

    } // namespace internal
} // namespace toml

#endif //ATTACK_TOML_H
